package com.heima.comment.service.impl;
import java.util.*;

import com.alibaba.nacos.client.utils.StringUtils;
import com.heima.comment.feign.ArticleFeign;
import com.heima.comment.feign.UserFeign;
import com.heima.comment.feign.WemediaFeign;
import com.heima.comment.service.CommentRepayService;
import com.heima.comment.service.CommentService;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.model.article.dtos.ArticleSimpleInfoDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApAuthor;
import com.heima.model.behavior.pojos.ApBehaviorEntry;
import com.heima.model.comment.dtos.*;
import com.heima.model.comment.pojos.ApComment;
import com.heima.model.comment.pojos.ApCommentLike;
import com.heima.model.comment.pojos.ApCommentRepay;
import com.heima.model.comment.vo.ApCommentInWmVo;
import com.heima.model.comment.vo.ApCommentRepayInWmVo;
import com.heima.model.comment.vo.ApCommentVo;
import com.heima.model.comment.vo.WmCommentVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.media.pojos.WmUser;
import com.heima.model.user.pojos.ApUser;
import com.heima.utils.threadlocal.AppThreadLocalUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.stream.Collectors;

/**
 * @作者 itcast
 * @创建日期 2021/2/21 10:06
 **/
@Service
@Slf4j
public class CommentServiceImpl implements CommentService {
    @Autowired
    UserFeign userFeign;

    @Autowired
    GreenTextScan greenTextScan;

    @Autowired
    MongoTemplate mongoTemplate;
    @Autowired
    private WemediaFeign wemediaFeign;
    @Autowired
    private ArticleFeign articleFeign;


    @Override
    public ResponseResult wmLike(CommentLikeDto dto) {
        ApUser user = AppThreadLocalUtils.getUser();
        if(user==null||user.getId()==null)
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        WmUser wmUser = wemediaFeign.findWmUserById(Long.valueOf(user.getId()));
        if (wmUser==null)
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        user.setId(wmUser.getApUserId());
        AppThreadLocalUtils.setUser(user);
        return like(dto);
    }

    @Override
    public ResponseResult changeTop(CommentChangeTopDto dto) {
        //检查参数
        if (ObjectUtils.isEmpty(dto)|| dto.getArticleId() == null || org.apache.commons.lang3.StringUtils.isBlank(dto.getCommentId()) || (dto.getOperation()!= 0 && dto.getOperation()!=1)){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //检查用户登录情况
        ApUser user = AppThreadLocalUtils.getUser();
        if (ObjectUtils.isEmpty(user) || user.getId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        WmUser wmuser = wemediaFeign.findWmUserById(Long.valueOf(user.getId()));
        if (ObjectUtils.isEmpty(wmuser)){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN,"获取自媒体用户信息失败");
        }
        //查询评论
        ApComment apComment = mongoTemplate.findById(dto.getCommentId(), ApComment.class);
        if (ObjectUtils.isEmpty(apComment)){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"评论不存在");
        }
        //检查文章是否存在以及是否是该作者所写
        List<ApArticle> apAticles = articleFeign.findArticleIdsByAuthorIdAndArticleId(Long.valueOf(wmuser.getApAuthorId()), dto.getArticleId());
        if (ObjectUtils.isEmpty(apAticles) || apAticles.size() == 0){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"您的文章库中不存在此文章，无权置顶评论");
        }
        Integer ord = apComment.getOrd();
        if (ord == 0 && dto.getOperation() == 1){
            apComment.setOrd(1);
            mongoTemplate.save(apComment);
        }
        if (ord == 1 && dto.getOperation() == 0){
            apComment.setOrd(0);
            mongoTemplate.save(apComment);
        }
        HashMap<String, Object> result = new HashMap<>();
        result.put("ord",apComment.getOrd());
        return ResponseResult.okResult(result);
    }


    @Override
    public ResponseResult deleteByIdAndArticleId(CommentDeleteDto dto) {
        //检查参数
        if (ObjectUtils.isEmpty(dto)|| dto.getArticleId() == null || org.apache.commons.lang3.StringUtils.isBlank(dto.getCommentId())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        ApUser user = AppThreadLocalUtils.getUser();
        if (ObjectUtils.isEmpty(user) || user.getId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        WmUser wmuser = wemediaFeign.findWmUserById(Long.valueOf(user.getId()));
        if (ObjectUtils.isEmpty(wmuser)){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN,"获取自媒体用户信息失败");
        }
        //查询评论
        ApComment apComment = mongoTemplate.findById(dto.getCommentId(), ApComment.class);
        if (ObjectUtils.isEmpty(apComment)){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"评论不存在");
        }
        //检查文章是否存在以及是否是该作者所写
        List<ApArticle> apAticles = articleFeign.findArticleIdsByAuthorIdAndArticleId(Long.valueOf(wmuser.getApAuthorId()), dto.getArticleId());
        if (ObjectUtils.isEmpty(apAticles) || apAticles.size() == 0){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"您的文章库中不存在此文章，无权删除评论");
        }
        mongoTemplate.remove(apComment);
        return ResponseResult.okResult();
    }




    @Override
    public ResponseResult findByWmUser(CommentDto dto) {
        //检查用户
        ApUser user = AppThreadLocalUtils.getUser();
        if (ObjectUtils.isEmpty(user) || user.getId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        WmUser wmuser = wemediaFeign.findWmUserById(Long.valueOf(user.getId()));
        if (ObjectUtils.isEmpty(wmuser)){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN,"获取自媒体用户信息失败");
        }
        //检查时间
        if (!ObjectUtils.isEmpty(dto) && dto.getMinDate() == null ){
            dto.setMinDate(new Date());
        }
        //根据用户获取APP用户ID
        ApUser apUser = userFeign.findUserById(Long.valueOf(wmuser.getApUserId()));
        if (ObjectUtils.isEmpty(apUser)){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"自媒体用户关联的APP用户不存在");
        }
        //根据用户查询用户所有文章ID和标题
        List<ArticleSimpleInfoDto> apArticles = articleFeign.findArticleIdsByAuthorId(Long.valueOf(wmuser.getApAuthorId()));
        if (ObjectUtils.isEmpty(apArticles)){
            return ResponseResult.okResult();
        }
        List<Long> articleIds = apArticles.stream().map(ArticleSimpleInfoDto::getId).collect(Collectors.toList());
        HashMap<Long, String> articleMap = new HashMap<>();
        for (ArticleSimpleInfoDto apArticle : apArticles) {
            articleMap.put(apArticle.getId(),apArticle.getTitle());
        }
        //查询所有文章关联的评论
        List<ApComment> apComments= mongoTemplate.find(Query.query(Criteria.where("entryId").in(articleIds).and("updatedTime").lt(dto.getMinDate())).with(Sort.by(Sort.Direction.DESC,"updatedTime")).limit(2), ApComment.class);
        if (ObjectUtils.isEmpty(apComments)){
            return ResponseResult.okResult();
        }
        //将每条评论封装成Vo
        List<ApCommentInWmVo> apCommentInWmVos = apComments.stream().map(apComment -> {
            //获取对评论的回复
            List<ApCommentRepay> apCommentRepays = mongoTemplate.find(Query.query(Criteria.where("commentId").is(apComment.getId())).with(Sort.by(Sort.Direction.DESC, "updatedTime")), ApCommentRepay.class);
            List<ApCommentRepayInWmVo> apCommentRepayInWmVos = apCommentRepays.stream().map(apCommentRepay -> {
                ApCommentRepayInWmVo apCommentRepayInWmVo = new ApCommentRepayInWmVo();
                apCommentRepayInWmVo.setContent(apCommentRepay.getContent());
                apCommentRepayInWmVo.setNickName(apCommentRepay.getAuthorName());
                return apCommentRepayInWmVo;
            }).collect(Collectors.toList());
            //判断该评论是否已被文章作者点赞
            List<ApCommentLike> apCommentLikes = mongoTemplate.find(Query.query(Criteria.where("authorId").is(apUser.getId()).and("commentId").is(apComment.getId())), ApCommentLike.class);
            boolean isLike = !(ObjectUtils.isEmpty(apCommentLikes) || apCommentLikes.isEmpty());
            //封装VO
            ApCommentInWmVo apCommentInWmVo = new ApCommentInWmVo();
            apCommentInWmVo.setId(apComment.getId());
            apCommentInWmVo.setNickName(apComment.getAuthorName());
            apCommentInWmVo.setAvatarUrl(apComment.getImage());
            apCommentInWmVo.setDate(apComment.getUpdatedTime());
            apCommentInWmVo.setContent(apComment.getContent());
            apCommentInWmVo.setReplies(apCommentRepayInWmVos);
            apCommentInWmVo.setReplayNum(apCommentInWmVo == null ? 0 : apCommentRepayInWmVos.size());
            apCommentInWmVo.setAdmireNum(apComment.getLikes());
            apCommentInWmVo.setArticleName(articleMap.get(apComment.getEntryId()));
            apCommentInWmVo.setArticleId(apComment.getEntryId());
            apCommentInWmVo.setIsLike(isLike);
            apCommentInWmVo.setShowReplyBox(false);
            apCommentInWmVo.setOrd(apComment.getOrd());
            return apCommentInWmVo;
        }).collect(Collectors.toList());
        //获取最小时间
        Date minDate = apComments.get(apComments.size() - 1).getUpdatedTime();
        //返回结果集map
        HashMap<String, Object> result = new HashMap<>();
        result.put("comments",apCommentInWmVos);
        result.put("minDate",minDate);
        return ResponseResult.okResult(result);
    }

    @Override
    public ResponseResult saveComment(CommentSaveDto dto) {
        log.info("saveComment params is {}",dto);
        // 1. 检查参数 (文章id  评论内容不能大于140字符)
        if(dto == null || dto.getArticleId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        if(StringUtils.isEmpty(dto.getContent()) || dto.getContent().length() > 140){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"评论内容不能大于140个字符");
        }
        // 2. 检查是否登录 , 远程查询用户信息
        ApUser user = AppThreadLocalUtils.getUser();
        if(user == null || user.getId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN,"登录之后才能评论");
        }
        ApUser userInfo = userFeign.findUserById(Long.valueOf(user.getId()));
        if(userInfo == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN,"获取用户信息失败");
        }
        // 3. 阿里云检测评论内容是否违规
        try {
            Map map = greenTextScan.greeTextScan(dto.getContent());
            String suggestion = (String)map.get("suggestion");
            if("block".equals(suggestion)){
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"评论有违规，请注意你的言辞");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("阿里云审核文本内容出现错误  {}",e.getCause());
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"内容审核出现异常");
        }
        // 4. 保存评论信息
        ApComment apComment = new ApComment();
        apComment.setAuthorId(userInfo.getId());
        apComment.setAuthorName(userInfo.getName());
        apComment.setEntryId(dto.getArticleId());
        apComment.setType((short)0);
        apComment.setContent(dto.getContent());
        apComment.setImage(userInfo.getImage());
        apComment.setLikes(0);
        apComment.setReply(0);
        apComment.setOrd(0);
        apComment.setFlag((short)0);
        apComment.setCreatedTime(new Date());
        apComment.setUpdatedTime(new Date());
        mongoTemplate.save(apComment);
        return ResponseResult.okResult();
    }


    @Resource(name = "likesLock")
    RLock likesLock; // 分布式锁对象


    @Override
    public ResponseResult like(CommentLikeDto dto) {
        // 1. 检查参数 (评论ID  operation 是否是 0 或 1  )
        if(dto == null || dto.getCommentId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        if(dto.getOperation() == null || ( dto.getOperation()!=0 && dto.getOperation() != 1)){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //    判断用户是否登录
        ApUser user = AppThreadLocalUtils.getUser();
        if(user == null || user.getId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        likesLock.lock();
        ApComment apComment;

        try {
            // 2. 根据评论ID评论信息查询出来
            apComment = mongoTemplate.findById(dto.getCommentId(), ApComment.class);
            if(apComment == null){
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"该评论不存在");
            }
            if(dto.getOperation() == 0){
                // 3. 如果点赞方式为 0 (点赞)
                // 3.1 将评论的点赞数+1
                apComment.setLikes(apComment.getLikes() + 1);
                mongoTemplate.save(apComment); // 修改该评论
                // 3.2 保存评论点赞信息
                ApCommentLike apCommentLike = new ApCommentLike();
                apCommentLike.setAuthorId(user.getId());
                apCommentLike.setCommentId(dto.getCommentId());
                apCommentLike.setOperation((short)0);
                mongoTemplate.save(apCommentLike);
            }else {
                // 4. 如果点赞方式为 1 (取消点赞)
                // 4.1 将评论的点赞数-1
                apComment.setLikes(apComment.getLikes()==0 ? 0 : apComment.getLikes() - 1);
                mongoTemplate.save(apComment); // 修改该评论
                // 4.2 删除评论点赞信息
                Query query = Query.query(Criteria.where("commentId").is(dto.getCommentId()).and("authorId").is(user.getId()));
                mongoTemplate.remove(query,ApCommentLike.class);
            }
        } finally {
            likesLock.unlock();
        }
        // 5. 返回结果 ( likes )    {   data : {  "likes" : 21 }}
        Map map = new HashMap();
        map.put("likes",apComment.getLikes());
        return ResponseResult.okResult(map);
    }

    @Override
    public ResponseResult findByArticleId(CommentDto dto) {
        log.info("查找用户文章评论列表方法执行了：dto->{}",dto);
        // 1. 检查参数 （文章id  最小时间  分页:20 ）
        if(dto == null || dto.getArticleId() == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        if(dto.getMinDate() == null){
            dto.setMinDate(new Date());
        }
        int size = 20; // 默认分页长度20

        // 2. 查询评论列表
        // ( 文章ID = ？  and  创建时间 < minDate   使用创建时间降序排序  取前20条数据)
        Query queryTop = Query.query(Criteria.where("entryId").is(dto.getArticleId())
                .and("createdTime").lt(dto.getMinDate()).and("ord").is(1))
                .with(Sort.by(Sort.Direction.DESC,"createdTime"))
                .limit(size)
                ;
        Query query = Query.query(Criteria.where("entryId").is(dto.getArticleId())
                .and("createdTime").lt(dto.getMinDate()).and("ord").is(0))
                .with(Sort.by(Sort.Direction.DESC,"createdTime"))
                .limit(size)
                ;
        // 评论列表
        List<ApComment> apComments = new ArrayList<>();
        //添加置顶评论
        apComments.addAll(mongoTemplate.find(queryTop, ApComment.class));
        //添加非置顶评论
        apComments.addAll(mongoTemplate.find(query, ApComment.class));
        //如果大于20，则取前20
        if (apComments.size()>20){
            apComments = apComments.stream().limit(20).collect(Collectors.toList());
        }
        // 3. 检查用户是否登录
        ApUser user = AppThreadLocalUtils.getUser();
        if(user == null || user.getId() == null){
            return ResponseResult.okResult(apComments);
        }
        // 4. 如果用户登录 , 需要判断当前评论中 哪些评论用户点过赞
        if(apComments!=null && apComments.size()>0){
            // 4.1 获取查询出来的评论ID列表  commentId in [ 1 , 3 ,5 ]   and authorId = [4]
            List<String> commentIdList = apComments.stream().map(ApComment::getId).collect(Collectors.toList());
            // 4.2 根据评论ID列表 及 当前登录用户ID 去mongo点赞集合查询
            Query findLikeQuery = Query.query(Criteria.where("commentId").in(commentIdList).and("authorId").is(user.getId()));
            // 4.3 得到当前评论列表中  我点过赞的信息
            List<ApCommentLike> apCommentLikes = mongoTemplate.find(findLikeQuery, ApCommentLike.class);
            // 点过赞的评论ID
            Set<String> myCommentIds = apCommentLikes.stream().map(ApCommentLike::getCommentId).collect(Collectors.toSet());
            // 将每一条评论转成vo对象， 如果是我评论过的需要将operation字段设置为0
            List<ApCommentVo> commentVoList = apComments.stream().map(comment -> {
                ApCommentVo commentVo = new ApCommentVo();
                BeanUtils.copyProperties(comment, commentVo);
                // 查看我所点过赞的评论中 是否包含当前评论ID
                if (myCommentIds.contains(comment.getId())) {
                    // 如果包含将operation设置为0
                    commentVo.setOperation((short) 0);
                }
                return commentVo;
            }).collect(Collectors.toList());
            return ResponseResult.okResult(commentVoList);
        }
        return ResponseResult.okResult(apComments);
    }

}
